package stcpscheculer;

import java.util.HashSet;
import java.util.Random;
import java.util.Set;

public class GASchedule {

    //TODO Generate Init Population
    //TODO Selection
    //TODO Crossover
    //TODO Mutation
    //TODO Termination
    private int size;
    private int Buses;
    private int Drivers;
    private int TimeSlot; // in hours
    private Population currentPopulation;
    private Population selectedPopulation;
    private Population nextPopulation;
    private int maxGenerations;
    private int numOfGenerations;
    private int chrLength;

    public GASchedule() {

        size = Configuration.getPopulationSize();
        Buses = Configuration.getBuses();
        Drivers = Configuration.getDrivers();
        TimeSlot = Configuration.getTimeSlot();
        maxGenerations = Configuration.getMaxGenerations();
        chrLength = Configuration.getChromosomeLength();
        
    }

    public Chromosome runGA(){
        Chromosome solution = new Chromosome();
        generatePopulation();
        
        // Print initial generation
        System.out.println("*** Generation ini ***");
        currentPopulation.PrintPopulation();
        solution = currentPopulation.bestSolution();
        System.out.print("Best solution: ");
        solution.PrintSolution();
        
        for(int i=1;i<maxGenerations;i++){
            //solution = new Chromosome();

            selection();
            crossover();
            mutation();
            currentPopulation.evaluatePopulation();
            
            // Print new Generation
            System.out.println("*** Generation " + i + " ***");
            currentPopulation.PrintPopulation();
            solution = currentPopulation.bestSolution();
            System.out.print("Best solution: ");
            solution.PrintSolution();

            if (solution.getAdaptation() >= Configuration.getDesiredFA()) { 
                System.out.println("Desired result achieved!");
                break;
           }
        }
        System.out.println("****** Solution ***********");
        return solution;
    }
    
    /**
     * Creates initial generation
     *
     */
    public void generatePopulation() {
        currentPopulation = new Population();
        currentPopulation.initPopulation();
        currentPopulation.evaluatePopulation();
    }

    /**
     * Selects chromosomes to produce next generation. Uses elitism
     */
    public void selection() {
        int[] selected; // indexes of selected chromosomes
        selected = new int[size - 3];
        selectedPopulation = new Population();
        addElite(); // adds 3 best from current to selected population
        currentPopulation.calcPsel();
        selected = currentPopulation.selectionRoulete();
        for (int i = 0; i < size - 3; i++) {
            selectedPopulation.Population[i + 3] = 
                    new Chromosome(currentPopulation.getChromosomeAt(selected[i]));
            //selectedPopulation.Population[i + 3] = currentPopulation.getChromosomeAt(selected[i]);
        }
    }

    /**
     * Sort current population and move 3 best solitions to selectedPopulation
     * 
     */
    public void addElite() {
        currentPopulation.SortPopulation();
        System.out.println("CurrentPopulation Sorted: ");
        currentPopulation.PrintPopulation();
        selectedPopulation.Population[0] = new Chromosome(currentPopulation.getChromosomeAt(0));
        //selectedPopulation.Population[0] = currentPopulation.getChromosomeAt(0);

        selectedPopulation.Population[1] = new Chromosome(currentPopulation.getChromosomeAt(1));
        //selectedPopulation.Population[1] = currentPopulation.getChromosomeAt(1);
        
        selectedPopulation.Population[2] = new Chromosome(currentPopulation.getChromosomeAt(2));
        //selectedPopulation.Population[2] = currentPopulation.getChromosomeAt(2);
        
        // test copying
//        System.out.println("Change original:");
//        currentPopulation.Population[2].init();
//        currentPopulation.Population[2].PrintSolution();
//        System.out.println("Copy:");
//        selectedPopulation.Population[2].PrintSolution();
        
        System.out.println("Elite:");
        selectedPopulation.Population[0].PrintSolution();
        selectedPopulation.Population[1].PrintSolution();
        selectedPopulation.Population[2].PrintSolution();

    }

    public void crossover() {
        int[] toCross = null;
        nextPopulation = new Population(selectedPopulation);
        //nextPopulation = selectedPopulation;
        
        //System.out.println("SelectedPopulation: ");
        //selectedPopulation.PrintPopulation();
        System.out.println("NextPopulation: ");
        nextPopulation.PrintPopulation();

        toCross = nextPopulation.crossRoulete();
        if(toCross!=null){
            Random generator = new Random();
            int CrossPoint;
            int pos1, pos2;
            for(int i=0;i<toCross.length;i+=2){
                pos1 = toCross[i];
                // if last chromosome without a pair, cross with first
                if(toCross.length%2 == 1 && i == (toCross.length-1))
                    pos2 = toCross[0];
                else                 
                    pos2 = toCross[i+1];

                System.out.print("Mum " + pos1 + ": ");
                nextPopulation.Population[pos1].PrintSolution();
                System.out.print("Dad " + pos2 + ": ");
                nextPopulation.Population[pos2].PrintSolution();
                Chromosome chr = new Chromosome();
                // choose a crosspoint randomly
                CrossPoint = generator.nextInt(chrLength);
                //cross pair toCross[i] & toCross[i+1]
                // copy toCross[i] to chr
                chr.setSolutionArr(0, chrLength, 
                        nextPopulation.Population[pos1].getSolutionArr());
                // copy first part of toCross[i+1] to toCross[i]
                nextPopulation.Population[pos1].setSolutionArr(0, CrossPoint, 
                        nextPopulation.Population[pos2].getSolutionArr());
                // copy second part of chr to toCross[i+1]
                nextPopulation.Population[pos2].setSolutionArr(0, 
                        CrossPoint, chr.getSolutionArr());         
                
                System.out.print("Kid "+ pos1 + ": ");
                nextPopulation.Population[pos1].PrintSolution();
                System.out.print("Kid "+ pos2 + ": ");
                nextPopulation.Population[pos2].PrintSolution();
            }
        }
        System.out.println("Post Crossover NextPopulation: ");
        nextPopulation.PrintPopulation();
    }
    
    // apply mutation to the new population and copy it to current 
    public void mutation() {
        nextPopulation.mutate();
        currentPopulation = new Population(nextPopulation);
    }
}
